package com.hup.minicontroller.common.bluetooth.handler;

import android.util.Log;
import android.util.SparseArray;

import com.alibaba.fastjson.JSON;
import com.hup.minicontroller.common.bluetooth.BtConstant;
import com.hup.minicontroller.common.bluetooth.handler.BtDataHandler.DataMethodInvoker;
import com.hup.minicontroller.common.bluetooth.handler.dataLIstener.IBtDataListener;
import com.hup.minicontroller.common.bluetooth.model.BaseBtDataPacket;
import com.hup.minicontroller.common.bluetooth.model.BtExceptionResponseData;
import com.hup.minicontroller.common.bluetooth.model.BtUnknownData;

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;

/**
 * 读取数据处理器,处理[粘包,数据读取,编解码]功能
 *
 * @author hugan
 * @date 2021/5/14
 */
public class BtDataSplitCodecHandler {
    private static final String TAG = BtDataSplitCodecHandler.class.getSimpleName();

    public static final BtDataSplitCodecHandler INSTANCE = new BtDataSplitCodecHandler();

    /**
     * 已注册的数据类型,用于 {@link #decode(DataInputStream)}
     */
    private final SparseArray<Class<? extends BaseBtDataPacket>> typeMap;
    private List<IBtDataListener> dataListeners;

    public BtDataSplitCodecHandler() {
        typeMap = new SparseArray<>();
        dataListeners = new ArrayList<>();
        registerType(BtExceptionResponseData.class);
    }

    /**
     * decode解码:byte[]转对象
     */
    public BaseBtDataPacket decode(DataInputStream inputStream) throws IOException {
        matchHead(inputStream);
        int type = inputStream.readInt();//数据类型
        int dataLength = inputStream.readInt();//数据长度
        byte[] bytes = new byte[dataLength];//数据体

        readStream(inputStream, dataLength, bytes);//读取数据
        return deserialize(type, dataLength, bytes); //反序列化
    }

    /**
     * 校验帧头,处理粘包;
     * 遇到错误帧头时,一直循环,直到读到帧头为止
     */
    private void matchHead(DataInputStream inputStream) throws IOException {
        int head;
        int errCount = 0;
        while ((head = inputStream.readInt()) != BtConstant.FRAME_HEAD) {
            if (errCount == 0) Log.w(TAG, "decode: 帧头异常:head=" + head);
            errCount++;
        }
        if (errCount != 0) {
            Log.w(TAG, "decode: 帧头恢复正常: 异常次数=" + errCount);
        }
    }

    /**
     * 不断循环读取inputStream,直到读完目标长度,处理粘包
     *
     * @param dataLength 目标长度
     */
    private void readStream(DataInputStream inputStream, int dataLength, byte[] bytes) throws IOException {
        /*
        读取指定长度的数据,
        read时,读到的数据长度是不由入参决定的,由实际网络情况而定,
        所以需要多次read,直到读完dataLength为止
         */
        int readLength = 0;
        while (readLength < dataLength) {
            readLength += inputStream.read(bytes, readLength, dataLength - readLength);
        }
    }

    /**
     * 反序列化
     */
    private BaseBtDataPacket deserialize(int type, int dataLength, byte[] bytes) {
        Class<? extends BaseBtDataPacket> clz = typeMap.get(type);
        if (clz == null) {
            Log.w(TAG, "decode: 解析数据异常,未知类型:" + type);
            BtUnknownData data = new BtUnknownData(type, bytes.length);
            for (IBtDataListener dataListener : dataListeners) dataListener.onReceived(data, dataLength);
            return data;
        } else {
            try {
                BaseBtDataPacket object = JSON.parseObject(bytes, clz);
                object.setBtDataLength(dataLength);
                for (IBtDataListener dataListener : dataListeners) dataListener.onReceived(object, dataLength);
                return object;
            } catch (Exception e) {
                throw new IllegalArgumentException("反序列化异常,type=" + type);
            }
        }
    }

    /**
     * 序列化,encode编码:对象转byte[],并写出数据(发送数据)
     */
    public void encodeAndSend(DataOutputStream dataOutputStream, BaseBtDataPacket packet) throws IOException {
        int type = packet.getType();
        byte[] bytes = JSON.toJSONBytes(packet);
        dataOutputStream.writeInt(BtConstant.FRAME_HEAD);
        dataOutputStream.writeInt(type);
        dataOutputStream.writeInt(bytes.length);
        dataOutputStream.write(bytes);
        for (IBtDataListener dataListener : dataListeners) dataListener.onSend(packet, bytes.length);
    }


    /**
     * @param dataListener {@link IBtDataListener}
     */
    public void addDataListener(IBtDataListener dataListener) {
        dataListeners.add(dataListener);
    }

    /**
     * 注册数据类型,用于解析/解码收到的数据 {@link #decode(DataInputStream)}
     *
     * @param clz public数据类,需要继承 {@link BaseBtDataPacket}
     */
    public int registerType(Class<? extends BaseBtDataPacket> clz) {
        try {
            BaseBtDataPacket packet = clz.newInstance();
            int type = packet.getType();
            Class<?> old = typeMap.get(type);
            if (old != null) {
                //throw new IllegalArgumentException("类型重复:" + type);
                if (Objects.equals(old, clz)) return type;
                Log.i(TAG, "registerType: 覆盖类型:" + type + ", old=" + old + ", new=" + clz);
            }
            typeMap.put(type, clz);
            return type;
        } catch (ReflectiveOperationException e) {
            throw new IllegalArgumentException("实例化异常:" + clz, e);
        }
    }

    public void unRegisterType(int type) {
        typeMap.remove(type);
    }

    public void registerType(Map<Class<? extends BaseBtDataPacket>, DataMethodInvoker> map) {
        for (Map.Entry<Class<? extends BaseBtDataPacket>, DataMethodInvoker> entry : map.entrySet()) {
            Class<? extends BaseBtDataPacket> aClass = entry.getKey();
            DataMethodInvoker dataMethodInvoker = entry.getValue();
            typeMap.put(dataMethodInvoker.getDataType(), aClass);
        }
    }

}
